package com.example.supermonkeysm.config;

import com.example.supermonkeysm.security.filter.*;
import com.example.supermonkeysm.security.utils.TokenUtils;
import com.example.supermonkeysm.security.service.impl.UserDetailsServiceImpl;
import lombok.AllArgsConstructor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.http.HttpMethod;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import org.springframework.security.web.SecurityFilterChain;
import org.springframework.security.web.util.matcher.AntPathRequestMatcher;
import org.springframework.web.cors.CorsConfiguration;
import org.springframework.web.cors.CorsConfigurationSource;
import org.springframework.web.cors.UrlBasedCorsConfigurationSource;

import java.util.List;

@Configuration//全局使用
@EnableWebSecurity//启用Web安全
public class SecurityConfig {
    private final UserDetailsServiceImpl userDetailsServiceImpl;
    private final TokenUtils tokenUtils;
    private final RedisTemplate<String, Object> redisTemplate;
    public SecurityConfig(UserDetailsServiceImpl userDetailServiceImpl, TokenUtils tokenUtils, RedisTemplate<String, Object> redisTemplate) {
        this.userDetailsServiceImpl = userDetailServiceImpl;
        this.tokenUtils = tokenUtils;
        this.redisTemplate = redisTemplate;
    }

    /**
     * 密码校验类型
     * @return
     */
    @Bean
    public BCryptPasswordEncoder bCryptPasswordEncoder() {
        return new BCryptPasswordEncoder();
    }

    /**
     * 自定义验证
     * @param httpSecurity
     * @return
     * @throws Exception
     */
    @Bean
    AuthenticationManager authenticationManager(HttpSecurity httpSecurity) throws Exception {
        return httpSecurity.getSharedObject(AuthenticationManagerBuilder.class)
                .userDetailsService(userDetailsServiceImpl)
                .passwordEncoder(bCryptPasswordEncoder())
                .and()
                .build();
    }


    //security解决跨域问题
    CorsConfigurationSource corsConfigurationSource(){
        CorsConfiguration corsConfiguration = new CorsConfiguration();
        corsConfiguration.setAllowedHeaders(List.of("*"));
        corsConfiguration.setAllowedMethods(List.of("*"));
        corsConfiguration.setAllowedOrigins(List.of("*"));
        corsConfiguration.setMaxAge(3600L);
        UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
        source.registerCorsConfiguration("/**",corsConfiguration);
        return source;
    }
    @Bean
    public SecurityFilterChain securityFilterChain(AuthenticationManager authenticationManager,HttpSecurity httpSecurity) throws Exception {
        httpSecurity.csrf().disable().cors().and()
                .authorizeHttpRequests(auth -> auth
                        .requestMatchers(HttpMethod.POST, "/register").permitAll()//注册
                        .anyRequest().authenticated()//其余请求都需要认证后访问
                )
                //表单登陆
                .formLogin(form->form
                        .successHandler(new LoginSuccessHandler(tokenUtils,redisTemplate))
                        .failureHandler(new LoginFailureHandler())
                )
                //认证权限的过滤器
                .addFilter(new AuthenticationFilter(authenticationManager, tokenUtils, redisTemplate))
                .exceptionHandling(exception -> exception
                        //未能通过认证，也就是未登录
                        .authenticationEntryPoint(new NoAuthentication())
                        //已认证但是权限不够
                        .accessDeniedHandler(new NoPermission())
                )
                //退出登录
                .logout(logout->logout
                        .logoutRequestMatcher(new AntPathRequestMatcher("/logout", "POST"))
                        .logoutSuccessHandler(new LogoutSuccess(tokenUtils, redisTemplate))
                )
                .sessionManagement().sessionCreationPolicy(SessionCreationPolicy.STATELESS);//禁用session
        return httpSecurity.build();
    }
}
